﻿namespace Common.Protocol
{
    using System;
    using System.Collections.Generic;

    public class Block
    {
        public static int SizeInBytes = 2048;

        #region public properties

        /// <summary>
        /// Unique block identifier
        /// </summary>
        public Int32 Id { get; private set; }

        /// <summary>
        /// Physical address of previous block header
        /// </summary>
        public Int32 PreviousBlockAddress { get; private set; }

        /// <summary>
        /// Virtual address of first data entry in current block
        /// </summary>
        public Int64 DataStartAddress { get; private set; }

        // ! - Адреса байта після кінця даного блока
        public Int32 DataEndAddress { get; private set; }

        /// <summary>
        /// Time interval in block
        /// </summary>
        public Int32 TotalMilliseconds { get; private set; }

        public byte EndLine;

        public List<Channel> Channels = new List<Channel>();

        public DateTime Start { get; private set; }

        public DateTime End { get; private set; }

        #endregion

        public bool ParseFromBinary(byte[] bytes, int offset)
        {
            int position = offset;
            int magic = BitConverter.ToInt32(bytes, position);
            if (magic != -129)
            {
                return false;
            }

            position += 4;
            this.Id = BitConverter.ToInt32(bytes, position);
            position += 4;
            this.PreviousBlockAddress = BitConverter.ToInt32(bytes, position);
            position += 4;
            //this.HeaderNext = BitConverter.ToInt32(bytes, position);
            position += 4;
            this.DataStartAddress = BitConverter.ToInt64(bytes, position);
            position += 8;
            this.DataEndAddress = BitConverter.ToInt32(bytes, position);
            position += 4;
            this.TotalMilliseconds = BitConverter.ToInt32(bytes, position);
            position += 4;
            Int64 startMs = BitConverter.ToInt64(bytes, position);
            position += 8;
            this.EndLine = bytes[position];
            
            position += 8;
            for (int i = 0; i < 32; i++)
            {
                Channel channel = new Channel(this);
                if (channel.ParseFromBinary(bytes, position, Channel.GetIdFromNumber(i)))
                {
                    this.Channels.Add(channel);
                }

                position += 52;
            }
            
            this.Start = new DateTime(startMs * 10000).AddYears(1969);
            this.End = this.Start.AddMilliseconds(this.TotalMilliseconds);
            return true;
        }

        public Block CopyTime(DateTime start, DateTime end)
        {
            Block block = new Block();
            block.Id = this.Id;
            if (start >= this.Start && start < this.End)
            {
                block.Start = start;
            }
            else
            {
                if (start > this.End)
                {
                    throw new ArgumentOutOfRangeException("start", @"out of block time bounds");
                }
                else
                {
                    block.Start = this.Start;
                }
            }

            if (end <= this.End && end > start)
            {
                block.End = end;
            }
            else
            {
                if (end <= start || end <= this.Start)
                {
                    throw new ArgumentOutOfRangeException("end", @"out of block time bounds");
                }
                else
                {
                    block.End = end;
                }
            }

            block.TotalMilliseconds = (int)(block.End - block.Start).TotalMilliseconds;

            foreach (var channel in this.Channels)
            {
                block.Channels.Add(channel.CopyTime(start, end));
            }

            return block;
        }

        public Block Copy(DateTime currentTime, long milliseconds)
        {
            long halfMilliseconds = milliseconds / 2;
            Block copy = new Block();
            if (this.Start >= currentTime.AddTicks(-halfMilliseconds * TimeSpan.TicksPerMillisecond))
            {
                copy.Start = this.Start;
            }
            else
            {
                copy.Start = currentTime.AddTicks(-halfMilliseconds * TimeSpan.TicksPerMillisecond);
            }

            if (this.End <= currentTime.AddMilliseconds(halfMilliseconds))
            {
                copy.End = this.End;
            }
            else
            {
                copy.End = currentTime.AddMilliseconds(halfMilliseconds);
            }

            foreach (var channel in this.Channels)
            {
                copy.Channels.Add(channel.CopyTime(copy.Start, copy.End));
            }

            copy.TotalMilliseconds = (int)(copy.End - copy.Start).TotalMilliseconds;
            copy.Id = this.Id;
            return copy;
        }

        public Block CopyFromStart(long milliseconds)
        {
            Block copy = new Block();
            copy.Start = this.Start;
            if (this.TotalMilliseconds >= milliseconds)
            {
                copy.End = copy.Start.AddMilliseconds(milliseconds);
            }
            else
            {
                copy.End = this.End;
            }

            foreach (var channel in this.Channels)
            {
                copy.Channels.Add(channel.CopyTime(copy.Start, copy.End));
            }

            copy.TotalMilliseconds = (int)(copy.End - copy.Start).TotalMilliseconds;
            copy.Id = this.Id;
            return copy;
        }

        public Block CopyFromEnd(long milliseconds)
        {
            Block copy = new Block();
            copy.End = this.End;
            if (this.TotalMilliseconds >= milliseconds)
            {
                copy.Start = copy.End.AddTicks(-milliseconds * TimeSpan.TicksPerMillisecond);
            }
            else
            {
                copy.Start = this.Start;
            }

            foreach (var channel in this.Channels)
            {
                copy.Channels.Add(channel.CopyTime(copy.Start, copy.End));
            }

            copy.TotalMilliseconds = (int)(copy.End - copy.Start).TotalMilliseconds;
            copy.Id = this.Id;
            return copy;
        }
    }
}